#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

static volatile sig_atomic_t sigflag;
static sigset_t newmask, oldmask, zeromask;

static void sig_usr(int signo)
{
    sigflag = 1;
}

void TELL_WAIT(void)
{
    if (signal(SIGUSR1, sig_usr) == SIG_ERR) 
    {
        perror("signal error\n");
        exit(0);
    }

    if (signal(SIGUSR2, sig_usr) == SIG_ERR) 
    {
        perror("signal error\n");
        exit(0);
    }

    sigemptyset(&zeromask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGUSR1);
    sigaddset(&newmask, SIGUSR2);

    if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
    {
        perror("sigprocmask error");
        exit(0);
    }
}

void TELL_PARENT(pid_t pid)
{
    kill(pid, SIGUSR2);
}

void WAIT_PARENT(void)
{
    while (sigflag == 0)
        sigsuspend(&zeromask);
    sigflag = 0;

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        perror("sigprocmask error");
        exit(0);
    }
}

void TELL_CHILD(pid_t pid)
{
    kill(pid, SIGUSR1);
}

void WAIT_CHILD(void)
{
    while (sigflag == 0)
        sigsuspend(&zeromask);
    sigflag = 0;

    if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
    {
        perror("sigprocmask error");
        exit(0);
    }
}

void read_write(int fd, char *str)
{
    char buf[1024];

    lseek(fd, 0, SEEK_SET);
    read(fd, buf, 1);
    buf[0] = buf[0] + 1;
    lseek(fd, 0, SEEK_SET);
    write(fd, buf, 1);
    strcpy(buf, str);
    lseek(fd, 0, SEEK_END);
    write(fd, buf, strlen(buf));
}

int main(int argc, char const* argv[])
{
    pid_t pid;
    int fd = open("file", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
    write(fd, "0\n", 2);

    TELL_WAIT();

    if ((pid = fork()) == 0)
    {
        WAIT_PARENT();
        read_write(fd, "write by child\n");
        TELL_PARENT(getppid());
        WAIT_PARENT();
        read_write(fd, "write by child\n");
        TELL_PARENT(getppid());

        exit(0);
    }

    read_write(fd, "write by parent\n");
    TELL_CHILD(pid);
    WAIT_CHILD();
    read_write(fd, "write by parent\n");
    TELL_CHILD(pid);
    WAIT_CHILD();

    return 0;
}
